package wntransform.transformation;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
 *
 * @author Jan Gabela
 */
public class CZToPLTransformation implements Transformation {

    
    @Override
    public void transformXML(String inputXMLPath, String outputXMLPath, String xslPath) throws TransformerConfigurationException, TransformerException, IOException, ParserConfigurationException, SAXException {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document docIn = db.parse(new File(inputXMLPath));
        Document docOut = db.newDocument();

        // map of pairs literal objects and theirs identifier number
        Map<LiteralObject, Long> lexicalUnits = new HashMap<LiteralObject, Long>();
        Long idGenerator = new Long(1);

        // parse LiteralObjects from input document without the duplicities
        NodeList literals = docIn.getElementsByTagName("LITERAL");
        for (int i = 0; i < literals.getLength(); i++) {
            Node literal = literals.item(i);
            LiteralObject lo = parseLexicalObject(literal);
            if (!lexicalUnits.containsKey(lo)) {
                lexicalUnits.put(lo, idGenerator++);
            }
        }

        // appends root element 'arraylist' to output document
        Element arrayList = docOut.createElement("array-list");
        // sets version attribute to arraylist element
        arrayList.setAttribute("version", "WordnetLoom 1.48");
        // appends arraylist element to to document, array list is root element of the document now
        docOut.appendChild(arrayList);

        // append lexical-unit elements to output document root element 'arraylist'
        for (LiteralObject lo : lexicalUnits.keySet()) {

            // creates lexical-unit element
            Element lexicalUnit = docOut.createElement("lexical-unit");

            // sets id and name attribute to lexical-unit element
            lexicalUnit.setAttribute("id", lexicalUnits.get(lo).toString());
            lexicalUnit.setAttribute("name", lo.getLiteral());

            // sets pos attribute to lexical-unit element
            String pos = lo.getPos();
            lexicalUnit.setAttribute("pos", pos);

            // sets variant attribute to lexical-unit element
            lexicalUnit.setAttribute("variant", Integer.toString(lo.getSense()));
            // appand lexical-unit element to root element 'arraylist'
            arrayList.appendChild(lexicalUnit);
        }

        // types of synset relations
        NodeList synRel = docIn.getElementsByTagName("TYPE");

        // map of pairs types of relations(String) and theirs id
        Map<String, Long> relationTypes = new HashMap<String, Long>();

        // reset id generator
        idGenerator = new Long(1);

        // relations between synsets
        for (int i = 0; i < synRel.getLength(); i++) {
            if (!relationTypes.containsKey(synRel.item(i).getTextContent())) {
                relationTypes.put(synRel.item(i).getTextContent(), idGenerator++);
            }
        }

        // appends all relationTypes to output document
        for (String s : relationTypes.keySet()) {
            // creates relationtypes element
            Element relationTypesElem = docOut.createElement("relationtypes");
            // sets id attribute to relationTypesElem element
            relationTypesElem.setAttribute("id", Long.toString(relationTypes.get(s)));
            // sets name attribute to relationTypesElem element
            relationTypesElem.setAttribute("name", s);

            // appends relationTypesElem element to arrayList elemenet
            arrayList.appendChild(relationTypesElem);
        }

        //parse SYNSET objects from input document
        NodeList synsets = docIn.getElementsByTagName("SYNSET");
        for (int i = 0; i < synsets.getLength(); i++) {
            // synsetElem - one synset element from input document
            Element synsetElem = (Element) synsets.item(i);
            // create new element 'synset'
            Element synset = docOut.createElement("synset");
            // set id attribute to synset element
			long id;
			try {
				id = parseSynsetID(synsetElem.getElementsByTagName("ID").item(0));
			} catch(IllegalArgumentException ex) {
				continue;
			}
            synset.setAttribute("id", Long.toString(id));

            // synsetLiterals is list of LITERAL elements from synsetElem (one synset element from input document)
            NodeList synsetLiterals = synsetElem.getElementsByTagName("LITERAL");
            for (int j = 0; j < synsetLiterals.getLength(); j++) {
                // parses literal object from literal node
                LiteralObject lo = parseLexicalObject(synsetLiterals.item(j));
                // creates unit-id element
                Element idElem = docOut.createElement("unit-id");
                // set text content for unit-id element
                idElem.setTextContent(Long.toString(lexicalUnits.get(lo)));
                // appands unit-id element to synset element
                synset.appendChild(idElem);
            }

            // appends synset element to root element 'arraylist'
            arrayList.appendChild(synset);

            // for current synset appends to array-list element all its synsetrelation
            // ILR nodes of SYNSET node
            NodeList ilr = synsetElem.getElementsByTagName("ILR");
            for (int j = 0; j < ilr.getLength(); j++) {
                // one ILR element
                Element ilrElem = (Element) ilr.item(j);

//				System.out.println("0: " + ilrElem.getChildNodes().item(0).getTextContent()
//						+ "; 1: " + ilrElem.getChildNodes().item(1).getTextContent());

                // creates synsetrelation element
                Element synsetRelation = docOut.createElement("synsetrelations");
                // set parent attribute
				try {
					id = parseSynsetID(ilrElem);
				} catch(IllegalArgumentException ex) {
					continue;
				}
                synsetRelation.setAttribute("parent", Long.toString(id));
                // set relation attribute
                if(relationTypes.get(ilrElem.getChildNodes().item(0).getTextContent()) == null) {
                    continue;
                }
                synsetRelation.setAttribute("relation", Long.toString(relationTypes.get(ilrElem.getChildNodes().item(0).getTextContent())));
                // set child attribute
				try {
					id = parseSynsetID(synsetElem.getElementsByTagName("ID").item(0));
				} catch(IllegalArgumentException ex) {
					continue;
				}
                synsetRelation.setAttribute("child", Long.toString(id));
                // sets empty attributes
                /*synsetRelation.setAttribute("owner", "");
                synsetRelation.setAttribute("valid", "");*/
                // appends synsetRelation element to arrayList element
                arrayList.appendChild(synsetRelation);
            }
        }

        Source src = new DOMSource(docOut);
        Result res = new StreamResult(new File(outputXMLPath));

        StreamSource xslSource = new StreamSource(new File(xslPath));

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer(xslSource);
        trans.transform(src, res);
    }

    /**
     * Parses the id of the given ID/ILR element to needed format
     * @param idNode  ILR/ID node from input document
     * @return ID of synset
     */
    private long parseSynsetID(Node idNode) {
		if(idNode == null) {
			throw new IllegalArgumentException("idNode");
		}
                if(!idNode.getNodeName().equals("ILR") || !idNode.getNodeName().equals("ID")) {
                        throw new IllegalArgumentException("idNode");
                }
		
		for(Node child = idNode.getFirstChild(); child != null; child = child.getNextSibling()) {
			if(child.getNodeType() == Node.TEXT_NODE) {
				String iDs = child.getNodeValue().split("-")[1];
				return Long.parseLong(iDs);
			}
		}
		throw new IllegalArgumentException("idNode");
    }

    /**
     * Parses the literal object element to needed format
     * @param idNode  ILR/ID node from input document
     * @return needed literal object
     */
    private LiteralObject parseLexicalObject(Node literalNode) {
        Node sense = literalNode.getChildNodes().item(1);
        Node pos = ((Element) literalNode.getParentNode().getParentNode()).getElementsByTagName("POS").item(0);

        LiteralObject lo = new LiteralObject(
                literalNode.getChildNodes().item(0).getTextContent(),
                Integer.parseInt(sense.getTextContent()),
                pos.getTextContent());
        return lo;
    }
}
